import json
import os.path
import random
import zipfile

# 数据包路径（存档路径下的datapacks文件夹）
DATAPACKS_FOLDER_PATH = "D:/"
# 维度：-1 地狱  0 主世界  1 末地
DIMENSION = -1
# 版本是否是1.16以上
LATER_16 = True
# 层数范围（y的范围）
BOTTOM_Y, TOP_Y = 114, 117
# 站点范围（录入的站点坐标前后左后的格数）
STATION_RANGE = 1
# 保存的数据包名（自动加zip）
DATAPACK_NAME = "LuckyIceTransportation2021"
# 版本号
VERSION = "1.0"
# 更新日期
UPDATE_TIME = "20211031"


# 以下内容请不要修改==========


def add_zip_to_filename(filename: str):
    if not filename.endswith(".zip"):
        filename += ".zip"
    return filename


def create_litdata_file(filepath):
    # 创建litdata
    with open(filepath, "w", encoding="utf-8") as f:
        json.dump({
            "stations": [],
            "crossroads": []
        }, f, indent=4, ensure_ascii=False)


def read_litdata_file(filepath):
    # 读入litdata
    if not os.path.exists(filepath):
        create_litdata_file(filepath)
    with open(filepath, "r", encoding="utf-8") as f:
        litdata = json.load(f)
    return litdata


def get_station_or_crossroad_by_pos(new_info, litdata):
    # 通过坐标获取站或路口
    stations = litdata["stations"]
    crossroads = litdata["crossroads"]

    for station in stations:
        if new_info["pos"] == station["pos"]:
            return station
    for crossroad in crossroads:
        if new_info["pos"] == crossroad["pos"]:
            return crossroad


def get_station_or_crossroad_by_name(new_info, litdata):
    # 通过名字获取站或路口
    stations = litdata["stations"]
    crossroads = litdata["crossroads"]

    for station in stations:
        if new_info["name"] == station["name"]:
            return station
    for crossroad in crossroads:
        if new_info["name"] == crossroad["name"]:
            return crossroad


def get_direction_of_pos(sx, sz, dx, dz):
    # 东0，南1，西2，北3
    if sx == dx:
        if sz - dz < 0:
            return 1
        else:
            return 3
    elif sz == dz:
        if sx - dx < 0:
            return 0
        else:
            return 2
    else:
        return -1


def get_auto_connect_roads(litdata):
    # 自动生成的区间列表
    stations = litdata["stations"]
    crossroads = litdata["crossroads"]
    total = stations + crossroads

    auto_roads = []
    l = len(total)
    for i in range(l):
        ed, sd, wd, nd = [2147483647] * 4
        er, sr, wr, nr = [[]] * 4
        for j in range(l):
            if i == j:
                continue
            direction = get_direction_of_pos(*total[i]["pos"], *total[j]["pos"])
            distance_x = abs(total[i]["pos"][0] - total[j]["pos"][0])
            distance_y = abs(total[i]["pos"][1] - total[j]["pos"][1])
            if direction == -1:
                continue
            elif direction == 0:
                # 东方向z不变
                if distance_x <= ed:
                    ed, er = distance_x, {"road": [total[i]["name"], total[j]["name"]], "distance": distance_x}
            elif direction == 1:
                # 南方向x不变
                if distance_y <= sd:
                    sd, sr = distance_y, {"road": [total[i]["name"], total[j]["name"]], "distance": distance_y}
            elif direction == 2:
                # 西方向z不变
                if distance_x <= wd:
                    wd, wr = distance_x, {"road": [total[i]["name"], total[j]["name"]], "distance": distance_x}
            elif direction == 3:
                # 北方向x不变
                if distance_y <= nd:
                    nd, nr = distance_y, {"road": [total[i]["name"], total[j]["name"]], "distance": distance_y}

        # 加到列表里（此处具有重复元素）
        if len(er) != 0:
            auto_roads.append(er)
        if len(sr) != 0:
            auto_roads.append(sr)
        if len(wr) != 0:
            auto_roads.append(wr)
        if len(nr) != 0:
            auto_roads.append(nr)

    res = []
    for auto_road in auto_roads:
        auto_road_pos_reverse = {
            "road": [auto_road["road"][1], auto_road["road"][0]],
            "distance": auto_road["distance"]
        }
        if auto_road not in res and auto_road_pos_reverse not in res:
            res.append(auto_road)

    return res


def get_next_steps(name, roads):
    # 获取可通行的下一步和距离
    res = []
    for road in roads:
        if road["road"][0] == name:
            res.append({
                "name": road["road"][1],
                "distance": road["distance"]
            })
        elif road["road"][1] == name:
            res.append({
                "name": road["road"][0],
                "distance": road["distance"]
            })
    return res


def get_stations_crossroads_names(litdata):
    return [station["name"] for station in litdata["stations"]] + [crossroad["name"] for crossroad in
                                                                   litdata["crossroads"]]


def get_road_distance_by_names(name1, name2, roads):
    # 获取区间距离（自己到自己为0
    if name1 == name2:
        return 0
    for road in roads:
        if road["road"] == [name1, name2] or \
                road["road"] == [name2, name1]:
            return road["distance"]

    return 2147483647


def get_floyd_shortest_road_dicts(roads, all_names):
    shortest_distance_dict = {}
    shortest_path_dict = {}

    # 初始化距离
    for name1 in all_names:
        shortest_distance_dict[name1] = {}
        for name2 in all_names:
            shortest_distance_dict[name1][name2] = get_road_distance_by_names(name1, name2, roads)

    # 初始化路径
    for name1 in all_names:
        shortest_path_dict[name1] = {}
        for name2 in all_names:
            shortest_path_dict[name1][name2] = name2

    for name in all_names:
        for name1 in all_names:
            for name2 in all_names:
                d = shortest_distance_dict[name1][name] + shortest_distance_dict[name][name2]
                if d < shortest_distance_dict[name1][name2]:
                    shortest_distance_dict[name1][name2] = d
                    shortest_path_dict[name1][name2] = shortest_path_dict[name1][name]

    return shortest_distance_dict, shortest_path_dict


def get_route(source, dest, shortest_distance_dict, shortest_path_dict):
    res = [source]
    shortest_distance = shortest_distance_dict[source][dest]
    while source != dest:
        source = shortest_path_dict[source][dest]
        res.append(source)

    return res, shortest_distance


def get_road_dict_by_names(name1, name2, roads):
    for road in roads:
        if road["road"] == [name1, name2] or road["road"] == [name2, name1]:
            return road


def get_random_string(length, exist_list, lower_only=False):
    while True:
        string = ""
        while len(string) != length:
            rand = random.randint(0, 127)
            if 0x30 <= rand <= 0x39 or 0x61 <= rand <= 0x7a:
                string += chr(rand)
            elif 0x41 <= rand <= 0x5a:
                if not lower_only:
                    string += chr(rand)

        if string not in exist_list:
            exist_list.append(string)
            return string


def get_random_int(exist_list):
    while True:
        number = random.randint(-2147483648, 2147483647)
        if number == 0:
            continue
        if number not in exist_list:
            exist_list.append(number)
            return number


def get_dimension_data_condition(later_16, dimension):
    if later_16:
        if dimension == -1:
            return "{Dimension: \"minecraft:the_nether\"}"
        elif dimension == 0:
            return "{Dimension: \"minecraft:overworld\"}"
        elif dimension == 1:
            return "{Dimension: \"minecraft:the_end\"}"
    else:
        if dimension == -1:
            return "{Dimension: -1}"
        elif dimension == 0:
            return "{Dimension: 0}"
        elif dimension == 1:
            return "{Dimension: 1}"


def get_directions_of_roads(
        stations,
        crossroads,
        roads,
        shortest_distance_dict,
        shortest_path_dict,
        litdata
):
    total = stations + crossroads

    dest_road_direction_dict = {}
    # 初始化
    for station in stations:
        name = station["name"]
        # 由于只需要计算区间数据，对于孤立的点没有必要存入，因此此处选择一个区间的两端
        # 字典的第一个下标作为目的地
        # 字典的第二个下标作为区间起始点
        # 字典的第三个下标作为区间终点
        dest_road_direction_dict[name] = {}
        for road in roads:
            name1, name2 = road["road"]
            dest_road_direction_dict[name][name1] = {}
            dest_road_direction_dict[name][name2] = {}

    # 计算区间行进方向：由任意站点到同一站点的路径得出，可能存在路径没有走过的区间，因此需进行确认
    for dest in stations:
        dest_name = dest["name"]
        for source in total:
            source_name = source["name"]
            route_line, route_distance = get_route(
                source_name,
                dest_name,
                shortest_distance_dict,
                shortest_path_dict
            )
            if len(route_line) == 1:
                continue

            # 对于路径的每段区间，获取方向
            for i in range(len(route_line) - 1):
                name1 = route_line[i]
                name2 = route_line[i + 1]

                s1 = get_station_or_crossroad_by_name({"name": name1}, litdata)
                s2 = get_station_or_crossroad_by_name({"name": name2}, litdata)

                direction = get_direction_of_pos(
                    *s1["pos"],
                    *s2["pos"]
                )

                dest_road_direction_dict[dest_name][name1][name2] = direction
                dest_road_direction_dict[dest_name][name2][name1] = direction

        # 区间审核：寻找未标记的区间
        for road in roads:
            name1, name2 = road["road"]
            try:
                dest_road_direction_dict[dest_name][name1][name2]
                dest_road_direction_dict[dest_name][name2][name1]
            except:
                distance1 = shortest_distance_dict[name1][dest_name]
                distance2 = shortest_distance_dict[name2][dest_name]
                direction = get_direction_of_pos(
                    *get_station_or_crossroad_by_name({"name": name1}, litdata)["pos"],
                    *get_station_or_crossroad_by_name({"name": name2}, litdata)["pos"]
                )
                if distance1 > distance2:
                    dest_road_direction_dict[dest_name][name1][name2] = direction
                    dest_road_direction_dict[dest_name][name2][name1] = direction
                else:
                    dest_road_direction_dict[dest_name][name1][name2] = (direction + 2) % 4
                    dest_road_direction_dict[dest_name][name2][name1] = (direction + 2) % 4

    return dest_road_direction_dict


def select_help():
    print(
        "h/help  获取该帮助\n"
        "a/add  进入添加模式\n"
        "d/del  进入删除模式\n"
        "w/write  保存信息（写入litdata）\n"
        "l/list  列出坐标信息"
        "p/pack  打包成数据包\n"
        "s/setting  设置\n"
        "q/quit/exit  退出程序"
    )


def select_add(litdata):
    def add_help():
        print(
            "h/help  获取该帮助\n"
            "s/station  进入添加站点坐标模式\n"
            "c/crossroad  进入添加路口坐标模式\n"
            "l/list  获取所有站和路口信息\n"
            "q/quit/exit  退出添加坐标模式"
        )

    def add_station():
        def add_station_help():
            print(
                "h/help  获取该帮助\n"
                "q/quit/exit  退出添加站点坐标模式\n"
                "<x坐标>,<z坐标>,<站名>  添加一个站点"
            )
            print("添加站点坐标模板：<x坐标>,<z坐标>,<站名>")
            print("如：100,200,主城")

        while True:
            add_station_input_value = input("litdg/add/station> ")
            station_split = add_station_input_value.split(",")

            if add_station_input_value in ["help", "h"]:
                add_station_help()

            elif add_station_input_value in ["quit", "exit", "q"]:
                break

            elif len(station_split) == 3:
                try:
                    # 不使用数字，方便后续的删除
                    int(station_split[2])
                    print("添加失败，请不要使用数字作为站名")
                    continue
                except ValueError:
                    pass

                if " " in station_split[2]:
                    print("站名请不要有空格")
                    continue
                try:
                    # 构造新字典
                    new_station_dict = {
                        "pos": [int(station_split[0]), int(station_split[1])],
                        "name": station_split[2]
                    }
                except ValueError:
                    print("格式错误，h或help查看帮助")
                    continue

                # 出现同坐标时拒绝添加
                exist_station_by_pos = get_station_or_crossroad_by_pos(new_station_dict, litdata)
                exist_station_by_name = get_station_or_crossroad_by_name(new_station_dict, litdata)
                if exist_station_by_pos is not None:
                    print("添加失败，条目：")
                    print("坐标：" + str(exist_station_by_pos["pos"]))
                    print("名字：" + exist_station_by_pos["name"])
                    print("已存在")
                    continue
                if exist_station_by_name is not None:
                    print("添加失败，条目：")
                    print("坐标：" + str(exist_station_by_name["pos"]))
                    print("名字：" + exist_station_by_name["name"])
                    print("已存在")
                    continue

                litdata["stations"].append(new_station_dict)

            elif add_station_input_value.strip() == "":
                pass

            else:
                print("未知指令，h或help查看帮助")

    def add_crossroad():
        def add_crossroad_help():
            print(
                "h/help  获取该帮助\n"
                "q/quit/exit  退出添加路口坐标模式\n"
                "<x坐标>,<z坐标>,<路口名>  添加一个路口"
            )
            print("添加路口坐标模板：<x坐标>,<z坐标>,<路口名>")
            print("如：300,400,路口1")

        while True:
            add_crossroad_input_value = input("litdg/add/crossroad> ")
            crossroad_split = add_crossroad_input_value.split(",")

            if add_crossroad_input_value in ["help", "h"]:
                add_crossroad_help()

            elif add_crossroad_input_value in ["quit", "exit", "q"]:
                break

            elif len(crossroad_split) == 3:
                try:
                    int(crossroad_split[2])
                    print("添加失败，请不要使用数字作为路口名")
                    continue
                except ValueError:
                    pass

                try:
                    new_crossroad_dict = {
                        "pos": [int(crossroad_split[0]), int(crossroad_split[1])],
                        "name": crossroad_split[2]
                    }
                except ValueError:
                    print("格式错误，h或help查看帮助")
                    continue

                exist_crossroad_by_pos = get_station_or_crossroad_by_pos(new_crossroad_dict, litdata)
                exist_crossroad_by_name = get_station_or_crossroad_by_name(new_crossroad_dict, litdata)
                if exist_crossroad_by_pos is not None:
                    print("添加失败，条目：")
                    print("坐标：" + str(exist_crossroad_by_pos["pos"]))
                    print("名字：" + exist_crossroad_by_pos["name"])
                    print("已存在")
                    continue
                if exist_crossroad_by_name is not None:
                    print("添加失败，条目：")
                    print("坐标：" + str(exist_crossroad_by_name["pos"]))
                    print("名字：" + exist_crossroad_by_name["name"])
                    print("已存在")

                litdata["crossroads"].append(new_crossroad_dict)

            elif add_crossroad_input_value.strip() == "":
                pass

            else:
                print("未知指令，h或help查看帮助")

    while True:
        input_value = input("litdg/add> ")
        if input_value in ["help", "h"]:
            add_help()

        elif input_value in ["quit", "exit", "q"]:
            break

        elif input_value in ["station", "s"]:
            add_station()

        elif input_value in ["crossroad", "c"]:
            add_crossroad()

        elif input_value in ["list", "l"]:
            select_list(litdata)

        elif input_value.strip() == "":
            pass

        else:
            print("未知指令，h或help查看帮助")


def select_del(litdata):
    def del_help():
        print(
            "h/help  获取该帮助\n"
            "q/quit/exit  退出删除坐标模式\n"
            "l/list  列出所有站点和路口\n"
            "<x坐标>,<z坐标>  删除一个站点或路口"
        )
        print("删除站点或路口坐标模板：<x坐标>,<z坐标>")
        print("如：100,200")

    while True:
        input_value = input("litdg/del> ")
        input_split = input_value.split(",")
        if input_value in ["help", "h"]:
            del_help()

        elif input_value in ["quit", "exit", "q"]:
            break

        elif input_value in ["list", "l"]:
            select_list(litdata)

        elif len(input_split) == 2:
            try:
                station_or_crossroad_dict = {
                    "pos": [int(input_split[0]), int(input_split[1])],
                    "name": "default"
                }
            except ValueError:
                print("格式错误，h或help查看帮助")
                continue

            exist_station_or_crossroad = get_station_or_crossroad_by_pos(station_or_crossroad_dict, litdata)

            if exist_station_or_crossroad is None:
                print("无该条目，删除失败")
                continue

            try:
                litdata["stations"].remove(exist_station_or_crossroad)
            except ValueError:
                pass

            try:
                litdata["crossroads"].remove(exist_station_or_crossroad)
            except ValueError:
                pass

        elif input_value.strip() == "":
            pass

        else:
            print("未知指令，h或help查看帮助")


def select_write(filepath, litdata):
    with open(filepath, "w", encoding="utf-8") as f:
        json.dump(litdata, f, indent=4, ensure_ascii=False)


def select_pack(datapack_path, datapack_name, version, update_time, litdata):
    stations = litdata["stations"]
    crossroads = litdata["crossroads"]
    roads = get_auto_connect_roads(litdata)
    all_names = get_stations_crossroads_names(litdata)
    shortest_distance_dict, shortest_path_dict = get_floyd_shortest_road_dicts(roads, all_names)

    exist_random_strings = []
    exist_random_int = []

    int_scoreboard_name = get_random_string(16, exist_random_strings)
    # int_scoreboard_name = "tint"
    namespace_name = get_random_string(16, exist_random_strings, lower_only=True)
    # namespace_name = "namespace"
    load_mcfunction_name = get_random_string(16, exist_random_strings, lower_only=True)
    # load_mcfunction_name = "load"
    tick_mcfunction_name = get_random_string(16, exist_random_strings, lower_only=True)
    # tick_mcfunction_name = "tick"
    trigger_scoreboard_name = get_random_string(16, exist_random_strings)
    # trigger_scoreboard_name = "ttrigger"
    rotation_scoreboard_name = get_random_string(16, exist_random_strings)
    # rotation_scoreboard_name = "trotation"
    int_init_name, int_init_value = get_random_string(16, exist_random_strings), get_random_int(exist_random_int)
    # int_init_name, int_init_value = "init", -1
    in_range_scoreboard_name = get_random_string(16, exist_random_strings)
    # in_range_scoreboard_name = "tinrange"
    last_in_range_scoreboard_name = get_random_string(16, exist_random_strings)
    # last_in_range_scoreboard_name = "tlastinrange"
    int_circle_angle_name = get_random_string(16, exist_random_strings)
    # int_circle_angle_name = "circle"
    dest_scoreboard_name = get_random_string(16, exist_random_strings)
    # dest_scoreboard_name = "tdest"
    x_scoreboard_name = get_random_string(16, exist_random_strings)
    # x_scoreboard_name = "tx"
    y_scoreboard_name = get_random_string(16, exist_random_strings)
    # y_scoreboard_name = "ty"
    z_scoreboard_name = get_random_string(16, exist_random_strings)
    # z_scoreboard_name = "tz"
    int_in_range_station_dict = {}
    # count = 1
    for station in stations:
        name = station["name"]
        int_in_range_station_dict[name] = {
            "confused_name": get_random_string(16, exist_random_strings),
            "confused_value": get_random_int(exist_random_int)
        }
        # int_in_range_station_dict[name] = {
        #     "confused_name": name,
        #     "confused_value": count
        # }
        # count += 1

    int_direction_name = [
        get_random_string(16, exist_random_strings),
        get_random_string(16, exist_random_strings),
        get_random_string(16, exist_random_strings),
        get_random_string(16, exist_random_strings)
    ]
    int_direction_value = [
        get_random_int(exist_random_int),
        get_random_int(exist_random_int),
        get_random_int(exist_random_int),
        get_random_int(exist_random_int)
    ]
    # int_direction_name = [
    #     "tou",
    #     "nan",
    #     "shi",
    #     "hoku"
    # ]
    # int_direction_value = [
    #     1110,
    #     1111,
    #     1112,
    #     1113
    # ]
    int_true_name, int_true_value = get_random_string(16, exist_random_strings), get_random_int(exist_random_int)
    # int_true_name, int_true_value = "true", 9999999
    int_false_name, int_false_value = get_random_string(16, exist_random_strings), get_random_int(exist_random_int)
    # int_false_name, int_false_value = "false", -99999
    int_guide_name, int_guide_value = get_random_string(16, exist_random_strings), get_random_int(exist_random_int)
    # int_guide_name, int_guide_value = "guide", 12345678

    with zipfile.ZipFile(datapack_path, "w") as z:
        # 数据包识别文件
        pack_mcmeta = json.dumps(
            {
                "pack": {
                    "pack_format": 4,
                    "description": {
                        "text": "",
                        "color": "gold",
                        "extra": [
                            {"text": datapack_name, "color": "green"},
                            "\n",
                            "版本：",
                            {"text": version, "color": "green"},
                            "\n",
                            "更新时间：",
                            {"text": update_time, "color": "green"}
                        ]
                    }
                }
            }
        )
        pack_mcmeta_path = "pack.mcmeta"
        print(f"写入{pack_mcmeta_path}：\n{pack_mcmeta}\n")
        z.writestr(pack_mcmeta_path, pack_mcmeta)

        # 创建load.json文件并加入内容
        load_json = json.dumps({
            "replace": False,
            "values": [f"{namespace_name}:{load_mcfunction_name}"]
        })
        load_json_path = "data/minecraft/tags/functions/load.json"
        print(f"写入{load_json_path}：\n{load_json}\n")
        z.writestr(load_json_path, load_json)

        # 创建tick.json文件并加入内容
        tick_json = json.dumps({
            "replace": False,
            "values": [f"{namespace_name}:{tick_mcfunction_name}"]
        })
        tick_json_path = f"data/minecraft/tags/functions/tick.json"
        print(f"写入{tick_json_path}：\n{tick_json}\n")
        z.writestr(tick_json_path, tick_json)

        # 写入load.mcfunction
        load_mcfunction = "execute run tellraw @a " + json.dumps({
            "text": "",
            "color": "gold",
            "extra": [
                {"text": datapack_name, "color": "green"},
                "已加载",
                "\n",
                "版本：",
                {"text": version, "color": "green"},
                "\n",
                "更新时间：",
                {"text": update_time, "color": "green"}
            ]
        })
        load_mcfunction_path = f"data/{namespace_name}/functions/{load_mcfunction_name}.mcfunction"
        print(f"写入{load_mcfunction_path}：\n{load_mcfunction}\n")
        z.writestr(load_mcfunction_path, load_mcfunction)

        # 写入tick.mcfunction
        tick_mcfunction = ""
        # 设定淡入淡出时间
        tick_mcfunction += "execute as @a run title @s times 0 5 0\n"
        # 坐标获取
        tick_mcfunction += f"execute as @a store result score @s {x_scoreboard_name} run data get entity @s Pos[0]\n"
        tick_mcfunction += f"execute as @a store result score @s {y_scoreboard_name} run data get entity @s Pos[1]\n"
        tick_mcfunction += f"execute as @a store result score @s {z_scoreboard_name} run data get entity @s Pos[2]\n"
        # 在服务范围内判断
        tick_mcfunction += f"execute as @a if data entity @s {get_dimension_data_condition(LATER_16, DIMENSION)} " \
                           f"if score @s {y_scoreboard_name} matches {BOTTOM_Y}..{TOP_Y} run " \
                           f"scoreboard players operation @s {in_range_scoreboard_name} = " \
                           f"${int_true_name} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a unless data entity @s {get_dimension_data_condition(LATER_16, DIMENSION)} " \
                           f"run scoreboard players operation @s {in_range_scoreboard_name} = " \
                           f"${int_false_name} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a unless score @s {y_scoreboard_name} matches {BOTTOM_Y}..{TOP_Y} " \
                           f"run scoreboard players operation @s {in_range_scoreboard_name} = " \
                           f"${int_false_name} {int_scoreboard_name}\n"

        # 站点判断，修改服务范围计分板值
        for station in stations:
            x_pos, z_pos = station["pos"]
            station_name = station["name"]
            tick_mcfunction += f"execute as @a if score @s {in_range_scoreboard_name} = ${int_true_name} {int_scoreboard_name} " \
                               f'if score @s {x_scoreboard_name} matches {x_pos - STATION_RANGE}..{x_pos + STATION_RANGE} ' \
                               f'if score @s {z_scoreboard_name} matches {z_pos - STATION_RANGE}..{z_pos + STATION_RANGE} ' \
                               f'run scoreboard players operation @s {in_range_scoreboard_name} = ' \
                               f'${int_in_range_station_dict[station_name]["confused_name"]} {int_scoreboard_name}\n'

        # 获取转向，转换至方向
        tick_mcfunction += f"execute as @a store result score @s {rotation_scoreboard_name} " \
                           f"run data get entity @s Rotation[0]\n"
        tick_mcfunction += f"execute as @a if score @s {rotation_scoreboard_name} matches -360..-1 " \
                           f"run scoreboard players operation @s {rotation_scoreboard_name} += " \
                           f"${int_circle_angle_name} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a if score @s {rotation_scoreboard_name} matches 45..135 run " \
                           f"scoreboard players operation @s {rotation_scoreboard_name} = " \
                           f"${int_direction_name[2]} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a if score @s {rotation_scoreboard_name} matches 135..225 run " \
                           f"scoreboard players operation @s {rotation_scoreboard_name} = " \
                           f"${int_direction_name[3]} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a if score @s {rotation_scoreboard_name} matches 225..315 run " \
                           f"scoreboard players operation @s {rotation_scoreboard_name} = " \
                           f"${int_direction_name[0]} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a if score @s {rotation_scoreboard_name} matches 315..360 run " \
                           f"scoreboard players operation @s {rotation_scoreboard_name} = " \
                           f"${int_direction_name[1]} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a if score @s {rotation_scoreboard_name} matches 0..45 run " \
                           f"scoreboard players operation @s {rotation_scoreboard_name} = " \
                           f"${int_direction_name[1]} {int_scoreboard_name}\n"

        # 在站，则显示大标题，否则，不显示标题
        for station in stations:
            name = station["name"]
            tick_mcfunction += f"execute as @a if score @s {in_range_scoreboard_name} = " \
                               f'${int_in_range_station_dict[name]["confused_name"]} {int_scoreboard_name} ' \
                               'run title @s title '
            tick_mcfunction += json.dumps({
                "text": name,
                "color": "dark_aqua"
            }) + "\n"

        # 导航信息，副标题
        dest_road_direction_dict = get_directions_of_roads(
            stations,
            crossroads,
            roads,
            shortest_distance_dict,
            shortest_path_dict,
            litdata
        )

        for road in roads:
            name1, name2 = road["road"]
            pos1x, pos1z = get_station_or_crossroad_by_name({"name": name1}, litdata)["pos"]
            pos2x, pos2z = get_station_or_crossroad_by_name({"name": name2}, litdata)["pos"]

            # 坐标1小2大
            if pos1x > pos2x:
                temp = pos1x
                pos1x = pos2x
                pos2x = temp
            if pos1z > pos2z:
                temp = pos1z
                pos1z = pos2z
                pos2z = temp

            # 除去路口和站点的坐标范围
            if pos1x == pos2x:
                pos1x -= STATION_RANGE
                pos2x += STATION_RANGE
                pos1z += STATION_RANGE + 1
                pos2z -= STATION_RANGE + 1
            elif pos1z == pos2z:
                pos1x += STATION_RANGE + 1
                pos2x -= STATION_RANGE + 1
                pos1z -= STATION_RANGE
                pos2z += STATION_RANGE

            # 对去往每个站的所有区间添加导航信息
            for dest in stations:
                dest_name = dest["name"]
                direction = dest_road_direction_dict[dest_name][name1][name2] + 4
                direction_reverse = (direction + 2) % 4

                # 掉头
                tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                                   f'${int_true_name} {int_scoreboard_name} if score @s {dest_scoreboard_name} = ' \
                                   f'${int_in_range_station_dict[dest_name]["confused_name"]} {int_scoreboard_name} ' \
                                   f'if score @s {rotation_scoreboard_name} = ' \
                                   f'${int_direction_name[direction_reverse]} {int_scoreboard_name} ' \
                                   f'if score @s {x_scoreboard_name} matches {pos1x}..{pos2x} ' \
                                   f'if score @s {z_scoreboard_name} matches {pos1z}..{pos2z} run ' \
                                   f'title @s subtitle '
                tick_mcfunction += json.dumps({
                    "text": "",
                    "color": "gold",
                    "extra": [
                        {"text": "↓", "color": "red"},
                        " 去往",
                        {"text": dest_name, "color": "dark_aqua"},
                        " ",
                        {"text": "请掉头 "},
                        {"text": "↓", "color": "red"}
                    ]
                })
                tick_mcfunction += '\n'

                # 看向左边
                tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                                   f'${int_true_name} {int_scoreboard_name} if score @s {dest_scoreboard_name} = ' \
                                   f'${int_in_range_station_dict[dest_name]["confused_name"]} {int_scoreboard_name} ' \
                                   f'if score @s {rotation_scoreboard_name} = ' \
                                   f'${int_direction_name[(direction + 1) % 4]} {int_scoreboard_name} ' \
                                   f'if score @s {x_scoreboard_name} matches {pos1x}..{pos2x} ' \
                                   f'if score @s {z_scoreboard_name} matches {pos1z}..{pos2z} run ' \
                                   f'title @s subtitle '
                tick_mcfunction += json.dumps({
                    "text": "",
                    "color": "gold",
                    "extra": [
                        {"text": "←", "color": "red"},
                        " 去往",
                        {"text": dest_name, "color": "dark_aqua"},
                        " ",
                        {"text": "请看向左侧 "},
                        {"text": "←", "color": "red"}
                    ]
                })
                tick_mcfunction += '\n'

                # 看向右边
                tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                                   f'${int_true_name} {int_scoreboard_name} if score @s {dest_scoreboard_name} = ' \
                                   f'${int_in_range_station_dict[dest_name]["confused_name"]} {int_scoreboard_name} ' \
                                   f'if score @s {rotation_scoreboard_name} = ' \
                                   f'${int_direction_name[(direction - 1) % 4]} {int_scoreboard_name} ' \
                                   f'if score @s {x_scoreboard_name} matches {pos1x}..{pos2x} ' \
                                   f'if score @s {z_scoreboard_name} matches {pos1z}..{pos2z} run ' \
                                   f'title @s subtitle '
                tick_mcfunction += json.dumps({
                    "text": "",
                    "color": "gold",
                    "extra": [
                        {"text": "→", "color": "red"},
                        " 去往",
                        {"text": dest_name, "color": "dark_aqua"},
                        " ",
                        {"text": "请看向右侧 "},
                        {"text": "→", "color": "red"}
                    ]
                })
                tick_mcfunction += '\n'

                # 获取比较长的那一段
                route1, distance1 = get_route(
                    name1,
                    dest_name,
                    shortest_distance_dict,
                    shortest_path_dict
                )
                route2, distance2 = get_route(
                    name2,
                    dest_name,
                    shortest_distance_dict,
                    shortest_path_dict
                )
                route_longer = route1 if len(route1) > len(route2) else route2

                if route_longer[0] == dest_name:
                    continue

                # 选择了区间两端中到达目的地更长的一条路径，因此此处route_longer必定具有两个元素
                if route_longer[1] == dest_name:
                    # 前方就是目的地
                    tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                                       f'${int_true_name} {int_scoreboard_name} if score @s {dest_scoreboard_name} = ' \
                                       f'${int_in_range_station_dict[dest_name]["confused_name"]} {int_scoreboard_name} ' \
                                       f'if score @s {rotation_scoreboard_name} = ' \
                                       f'${int_direction_name[direction % 4]} {int_scoreboard_name} ' \
                                       f'if score @s {x_scoreboard_name} matches {pos1x}..{pos2x} ' \
                                       f'if score @s {z_scoreboard_name} matches {pos1z}..{pos2z} run ' \
                                       f'title @s subtitle '
                    tick_mcfunction += json.dumps({
                        "text": "",
                        "color": "gold",
                        "extra": [
                            {"text": "↑", "color": "red"},
                            " 前方到达",
                            {"text": dest_name, "color": "dark_aqua"},
                            " ",
                            {"text": "↑", "color": "red"}
                        ]
                    })
                    tick_mcfunction += '\n'
                else:
                    # 不是目的地
                    next_name1 = route_longer[1]
                    next_name2 = route_longer[2]

                    # 符合该条件的时候说明该路未被走过，需要使用另外的方法查找路线
                    if not (name1 in route_longer and name2 in route_longer):
                        r1, d1 = get_route(name1, dest_name, shortest_distance_dict, shortest_path_dict)
                        r2, d2 = get_route(name2, dest_name, shortest_distance_dict, shortest_path_dict)
                        rs = r1 if d1 < d2 else r2
                        next_direction = dest_road_direction_dict[dest_name][rs[0]][rs[1]] + 4

                    # 代表路径中有这个区间，可以直接获取方向
                    else:
                        next_direction = dest_road_direction_dict[dest_name][next_name1][next_name2] + 4

                    # 与原本方向相同
                    if next_direction % 4 == direction % 4:
                        tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                                           f'${int_true_name} {int_scoreboard_name} if score @s {dest_scoreboard_name} = ' \
                                           f'${int_in_range_station_dict[dest_name]["confused_name"]} {int_scoreboard_name} ' \
                                           f'if score @s {rotation_scoreboard_name} = ' \
                                           f'${int_direction_name[direction % 4]} {int_scoreboard_name} ' \
                                           f'if score @s {x_scoreboard_name} matches {pos1x}..{pos2x} ' \
                                           f'if score @s {z_scoreboard_name} matches {pos1z}..{pos2z} run ' \
                                           f'title @s subtitle '
                        tick_mcfunction += json.dumps({
                            "text": "",
                            "color": "gold",
                            "extra": [
                                {"text": "↑", "color": "red"},
                                " 去往",
                                {"text": dest_name, "color": "dark_aqua"},
                                " ",
                                {"text": "前方直行 "},
                                {"text": "↑", "color": "red"}
                            ]
                        })
                        tick_mcfunction += '\n'
                    # 右转
                    elif (direction + 1) % 4 == next_direction % 4:
                        tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                                           f'${int_true_name} {int_scoreboard_name} if score @s {dest_scoreboard_name} = ' \
                                           f'${int_in_range_station_dict[dest_name]["confused_name"]} {int_scoreboard_name} ' \
                                           f'if score @s {rotation_scoreboard_name} = ' \
                                           f'${int_direction_name[direction % 4]} {int_scoreboard_name} ' \
                                           f'if score @s {x_scoreboard_name} matches {pos1x}..{pos2x} ' \
                                           f'if score @s {z_scoreboard_name} matches {pos1z}..{pos2z} run ' \
                                           f'title @s subtitle '
                        tick_mcfunction += json.dumps({
                            "text": "",
                            "color": "gold",
                            "extra": [
                                {"text": "↑→", "color": "red"},
                                " 去往",
                                {"text": dest_name, "color": "dark_aqua"},
                                " ",
                                {"text": "前方右转 "},
                                {"text": "↑→", "color": "red"}
                            ]
                        })
                        tick_mcfunction += '\n'

                    # 左转
                    elif (direction - 1) % 4 == next_direction % 4:
                        tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                                           f'${int_true_name} {int_scoreboard_name} if score @s {dest_scoreboard_name} = ' \
                                           f'${int_in_range_station_dict[dest_name]["confused_name"]} {int_scoreboard_name} ' \
                                           f'if score @s {rotation_scoreboard_name} = ' \
                                           f'${int_direction_name[direction % 4]} {int_scoreboard_name} ' \
                                           f'if score @s {x_scoreboard_name} matches {pos1x}..{pos2x} ' \
                                           f'if score @s {z_scoreboard_name} matches {pos1z}..{pos2z} run ' \
                                           f'title @s subtitle '
                        tick_mcfunction += json.dumps({
                            "text": "",
                            "color": "gold",
                            "extra": [
                                {"text": "←↑", "color": "red"},
                                " 去往",
                                {"text": dest_name, "color": "dark_aqua"},
                                " ",
                                {"text": "前方左转 "},
                                {"text": "←↑", "color": "red"}
                            ]
                        })
                        tick_mcfunction += '\n'

        # 清除不该显示标题的，包括副标题
        tick_mcfunction += f"execute as @a if score @s {in_range_scoreboard_name} = " \
                           f"${int_true_name} {int_scoreboard_name} " \
                           f'run title @s title ""\n'
        tick_mcfunction += f"execute as @a unless score @s {in_range_scoreboard_name} = " \
                           f"${int_true_name} {int_scoreboard_name} " \
                           f'run title @s subtitle ""\n'
        tick_mcfunction += f'execute as @a '
        for station in stations:
            name = station["name"]
            tick_mcfunction += f'unless score @s {dest_scoreboard_name} = ' \
                               f'${int_in_range_station_dict[name]["confused_name"]} {int_scoreboard_name} '
        tick_mcfunction += 'run title @s subtitle ""\n'
        for crossroad in crossroads:
            name = crossroad["name"]
            posx, posz = crossroad["pos"]
            tick_mcfunction += f'execute as @a if score @s {in_range_scoreboard_name} = ' \
                               f'${int_true_name} {int_scoreboard_name} if score @s {x_scoreboard_name} ' \
                               f'matches {posx - STATION_RANGE}..{posx + STATION_RANGE} if score @s {z_scoreboard_name} ' \
                               f'matches {posz - STATION_RANGE}..{posz + STATION_RANGE} run ' \
                               f'title @s subtitle ""\n'

        # 进入交通时候弹出提醒（未选择导航）
        tick_mcfunction += f"execute as @a unless score @s {in_range_scoreboard_name} = " \
                           f"@s {last_in_range_scoreboard_name} if score @s {last_in_range_scoreboard_name} = " \
                           f"${int_false_name} {int_scoreboard_name} "
        for station in stations:
            name = station["name"]
            tick_mcfunction += f"unless score @s {dest_scoreboard_name} = " \
                               f'${int_in_range_station_dict[name]["confused_name"]} {int_scoreboard_name} '
        tick_mcfunction += f"run trigger {trigger_scoreboard_name} set {int_guide_value}\n"

        # 进入交通时候弹出提醒（已选择导航）
        for station in stations:
            name = station["name"]
            # 如果选择了站，则直接使用trigger指令
            tick_mcfunction += f"execute as @a unless score @s {in_range_scoreboard_name} = " \
                               f"@s {last_in_range_scoreboard_name} if score @s {last_in_range_scoreboard_name} = " \
                               f"${int_false_name} {int_scoreboard_name} " \
                               f"if score @s {dest_scoreboard_name} = " \
                               f'${int_in_range_station_dict[name]["confused_name"]} {int_scoreboard_name} run ' \
                               f'trigger {trigger_scoreboard_name} set {int_in_range_station_dict[name]["confused_value"]}\n'
        # trigger
        tick_mcfunction += f"execute as @a run scoreboard players enable @s {trigger_scoreboard_name}\n"

        # 对应trigger
        # 当点击了导航后执行的内容，发送提示信息和取消导航按钮
        for station in stations:
            name = station["name"]
            tick_mcfunction += f"execute as @a if score @s {trigger_scoreboard_name} = " \
                               f'${int_in_range_station_dict[name]["confused_name"]} {int_scoreboard_name} run ' \
                               f'tellraw @s '
            tick_mcfunction += json.dumps({
                "text": "当前导航到：",
                "color": "gold",
                "extra": [
                    {"text": name, "color": "dark_aqua"},
                    " ",
                    {
                        "text": "[取消导航]",
                        "color": "dark_purple",
                        "hoverEvent": {
                            "action": "show_text",
                            "value": {
                                "text": "点击此处：",
                                "color": "gold",
                                "extra": [{"text": "取消导航", "color": "dark_purple"}]
                            }
                        },
                        "clickEvent": {
                            "action": "run_command",
                            "value": f"/trigger {trigger_scoreboard_name} set {int_false_value}"
                        }
                    }
                ]
            })
            tick_mcfunction += "\n"

        # 选择对应站点后，修改dest的值
        for station in stations:
            name = station["name"]
            tick_mcfunction += f"execute as @a if score @s {trigger_scoreboard_name} = " \
                               f'${int_in_range_station_dict[name]["confused_name"]} {int_scoreboard_name} run ' \
                               f'scoreboard players operation @s {dest_scoreboard_name} = ' \
                               f'${int_in_range_station_dict[name]["confused_name"]} {int_scoreboard_name}\n'

        # 取消导航trigger，要先修改dest板内容再修改trigger
        tick_mcfunction += f"execute as @a if score @s {trigger_scoreboard_name} = " \
                           f"${int_false_name} {int_scoreboard_name} run " \
                           f"scoreboard players operation @s {dest_scoreboard_name} = " \
                           f"${int_false_name} {int_scoreboard_name}\n"
        tick_mcfunction += f"execute as @a if score @s {trigger_scoreboard_name} = " \
                           f"${int_false_name} {int_scoreboard_name} run " \
                           f"trigger {trigger_scoreboard_name} set {int_guide_value}\n"

        # 列出所有可导航的点的trigger
        tick_mcfunction += f"execute as @a if score @s {trigger_scoreboard_name} = " \
                           f"${int_guide_name} {int_scoreboard_name} run tellraw @s "
        extra = []
        for station in stations:
            name = station["name"]
            extra.append({
                "text": "[",
                "color": "dark_aqua"
            })
            extra.append({
                "text": name,
                "color": "dark_aqua",
                "hoverEvent": {
                    "action": "show_text",
                    "value": {
                        "text": "点击此处导航到：",
                        "color": "gold",
                        "extra": [
                            {"text": name, "color": "dark_aqua"}
                        ]
                    }
                },
                "clickEvent": {
                    "action": "run_command",
                    "value": f'/trigger {trigger_scoreboard_name} set {int_in_range_station_dict[name]["confused_value"]}'
                }
            })
            extra.append({
                "text": "]",
                "color": "dark_aqua"
            })
            extra.append(" ")
        tick_mcfunction += json.dumps({
            "text": "选择导航：\n",
            "color": "gold",
            "extra": extra
        })
        tick_mcfunction += "\n"

        # trigger reset
        tick_mcfunction += f"execute as @a run scoreboard players operation @s {trigger_scoreboard_name} = " \
                           f"${int_init_name} {int_scoreboard_name}\n"
        # 给last_in_range赋值
        tick_mcfunction += f"execute as @a run scoreboard players operation @s {last_in_range_scoreboard_name} = " \
                           f"@s {in_range_scoreboard_name}\n"
        tick_mcfunction_path = f"data/{namespace_name}/functions/{tick_mcfunction_name}.mcfunction"
        print(f"写入{tick_mcfunction_path}：\n{tick_mcfunction}\n")
        z.writestr(tick_mcfunction_path, tick_mcfunction)

        # 写入create.mcfunction
        create_mcfunction = f"execute run scoreboard objectives add {int_scoreboard_name} dummy\n"
        for i in range(4):
            create_mcfunction += f"execute run scoreboard players set ${int_direction_name[i]} {int_scoreboard_name} {int_direction_value[i]}\n"
        create_mcfunction += f"execute run scoreboard players set ${int_circle_angle_name} {int_scoreboard_name} 360\n"
        create_mcfunction += f"execute run scoreboard players set ${int_init_name} {int_scoreboard_name} {int_init_value}\n"
        create_mcfunction += f"execute run scoreboard players set ${int_true_name} {int_scoreboard_name} {int_true_value}\n"
        create_mcfunction += f"execute run scoreboard players set ${int_false_name} {int_scoreboard_name} {int_false_value}\n"
        create_mcfunction += f"execute run scoreboard players set ${int_guide_name} {int_scoreboard_name} {int_guide_value}\n"
        for station in stations:
            name = station["name"]
            create_mcfunction += f'execute run scoreboard players set ${int_in_range_station_dict[name]["confused_name"]} ' \
                                 f'{int_scoreboard_name} {int_in_range_station_dict[name]["confused_value"]}\n'
        create_mcfunction += f"execute run scoreboard objectives add {trigger_scoreboard_name} trigger\n"
        create_mcfunction += f"execute run scoreboard objectives add {in_range_scoreboard_name} dummy\n"
        create_mcfunction += f"execute run scoreboard objectives add {last_in_range_scoreboard_name} dummy\n"
        create_mcfunction += f"execute run scoreboard objectives add {x_scoreboard_name} dummy\n"
        create_mcfunction += f"execute run scoreboard objectives add {y_scoreboard_name} dummy\n"
        create_mcfunction += f"execute run scoreboard objectives add {z_scoreboard_name} dummy\n"
        create_mcfunction += f"execute run scoreboard objectives add {rotation_scoreboard_name} dummy\n"
        create_mcfunction += f"execute run scoreboard objectives add {dest_scoreboard_name} dummy\n"
        create_mcfunction_path = "data/lit/functions/create.mcfunction"
        print(f"写入{create_mcfunction_path}：\n{create_mcfunction}\n")
        z.writestr(create_mcfunction_path, create_mcfunction)

        # 写入clear.mcfunction
        clear_mcfunction = f"execute run scoreboard objectives remove {int_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {trigger_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {in_range_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {last_in_range_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {x_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {y_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {z_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {rotation_scoreboard_name}\n"
        clear_mcfunction += f"execute run scoreboard objectives remove {dest_scoreboard_name}\n"
        clear_mcfunction_path = "data/lit/functions/clear.mcfunction"
        print(f"写入{clear_mcfunction_path}：\n{clear_mcfunction}\n")
        z.writestr(clear_mcfunction_path, clear_mcfunction)

        print(f"写入完成，已保存为：{datapack_path}")


def select_setting():
    pass


def select_list(litdata):
    stations = litdata["stations"]
    crossroads = litdata["crossroads"]

    if len(stations) != 0:
        print("站点：")
        print("%10s%10s%20s" % ("x", "z", "站名"))
        for station in stations:
            poss = station["pos"]
            name = station["name"]
            print("%10d%10d%20s" % (poss[0], poss[1], name))

    if len(crossroads) != 0:
        print()
        print("路口：")
        print("%10s%10s%20s" % ("x", "z", "路口名"))
        for crossroad in crossroads:
            poss = crossroad["pos"]
            name = crossroad["name"]
            print("%10d%10d%20s" % (poss[0], poss[1], name))


def main():
    litdata_path = DATAPACKS_FOLDER_PATH + "/litdata_" + DATAPACK_NAME + ".json"
    datapack_path = DATAPACKS_FOLDER_PATH + "/" + add_zip_to_filename(DATAPACK_NAME + "_" + VERSION + "_" + UPDATE_TIME)

    litdata = read_litdata_file(litdata_path)
    print("数据包所在路径：" + DATAPACKS_FOLDER_PATH)
    print("冰道交通维度：" + str(DIMENSION) + "（-1 地狱  0 主世界  1 末地）")
    print("数据包名：" + DATAPACK_NAME)
    print("在任何时候，都可以输入help或者h获取当前操作帮助")
    print("在任何时候，都可以输入quit或exit或q返回上一级")
    print("在任何时候，没有任何信息提示时则表示操作成功")
    while True:
        input_value = input("litdg> ")
        if input_value in ["help", "h"]:
            select_help()

        elif input_value in ["add", "a"]:
            select_add(litdata)

        elif input_value in ["del", "d"]:
            select_del(litdata)

        elif input_value in ["list", "l"]:
            select_list(litdata)

        elif input_value in ["write", "w"]:
            select_write(litdata_path, litdata)

        elif input_value in ["pack", "p"]:
            select_pack(datapack_path, DATAPACK_NAME, VERSION, UPDATE_TIME, litdata)

        elif input_value in ["setting", "s"]:
            select_setting()

        elif input_value in ["quit", "exit", "q"]:
            break

        elif input_value.strip() == "":
            pass

        else:
            print("未知指令，h或help查看帮助")


if __name__ == '__main__':
    main()
